use std::{mem::transmute, ptr};

use jni::{objects::JByteArray, JNIEnv};

use crate::{error::AppErr, utils::{
    codec::{encode_u16, encode_u8},
    new_buf, xor_sum,
}};

pub mod ctype {

    pub const ACK: u8 = 0;
    pub const PING: u8 = 1;
    pub const PONG: u8 = 2;
    pub const SIMPLE_REQ: u8 = 3;
    pub const SIMPLE_RES: u8 = 4;
    pub const SESSION: u8 = 5;
    pub const REQ: u8 = 6;
    pub const RES: u8 = 7;
    pub const NOTIFY: u8 = 8;
    pub const NOTIFY_ACK: u8 = 9;
}

pub mod frame {

    pub const HEAD0: u8 = 0xE1;
    pub const HEAD1: u8 = 0x1E;
    pub const END: u8 = 0xEF;
    pub const HEAD: u16 = 0xE11E;

    pub const MIN_LEN: usize = 7;
}

pub mod cmd {

    pub const QUERY_TASK: u8 = 0x00;
}

pub fn make(ctype: u8, seq: u8, cmd: u8, data: &[u8]) -> Box<[u8]> {
    let len = frame::MIN_LEN + data.len() + 1;
    let mut buf = new_buf(len);
    encode_u16(&mut buf, frame::HEAD);
    encode_u8(&mut buf[2..], len as u8);
    encode_u8(&mut buf[3..], ctype);
    encode_u8(&mut buf[4..], seq);
    encode_u8(&mut buf[5..], cmd);
    unsafe {
        let dst = &mut buf[6..];
        ptr::copy_nonoverlapping(data.as_ptr(), dst.as_mut_ptr(), data.len());
    }
    let s = xor_sum(&buf[3..(len - 2)]);
    encode_u8(&mut buf[(len - 2)..], s);
    encode_u8(&mut buf[(len - 1)..], frame::END);
    buf
}

pub fn make_empty(ctype: u8, seq: u8) -> [u8; frame::MIN_LEN] {
    let mut buf = [0u8; frame::MIN_LEN];

    encode_u16(&mut buf, frame::HEAD);
    encode_u8(&mut buf[2..], frame::MIN_LEN as u8);
    encode_u8(&mut buf[3..], ctype);
    encode_u8(&mut buf[4..], seq);

    let s = xor_sum(&buf[3..(frame::MIN_LEN - 2)]);
    encode_u8(&mut buf[(frame::MIN_LEN - 2)..], s);
    encode_u8(&mut buf[(frame::MIN_LEN - 1)..], frame::END);
    buf
}

pub fn make_query_task(seq: u8, dst_seq: u8) -> [u8; frame::MIN_LEN + 2] {
    let mut buf = [0u8; frame::MIN_LEN + 2];
    encode_u16(&mut buf, frame::HEAD);
    encode_u8(&mut buf[2..], frame::MIN_LEN as u8);
    encode_u8(&mut buf[3..], ctype::SIMPLE_REQ);
    encode_u8(&mut buf[4..], seq);

    encode_u8(&mut buf[5..], cmd::QUERY_TASK);
    encode_u8(&mut buf[6..], dst_seq);

    let s = xor_sum(&buf[3..7]);
    encode_u8(&mut buf[..7], s);
    encode_u8(&mut buf[8..], frame::END);
    buf
}

pub struct RecvFrame {
    buf: Box<[u8]>,
}

impl RecvFrame {

    pub fn new(buf: Box<[u8]>) -> Self {
        Self { buf }
    }

    pub fn ctype(&self) -> u8 {
        self.buf[0]
    }

    pub fn seq(&self) -> u8 {
        self.buf[1]
    }

    pub fn id(&self) -> u16 {
        ((self.ctype() as u16) << 8) +
        (self.seq() as u16)
    }

    pub fn cmd(&self) -> u8 {
        self.buf[2]
    }

    pub fn ec(&self) -> u8 {
        self.buf[3]
    }

    pub fn new_jbyte_array2<'a>(&self, env: &'a mut JNIEnv) -> Result<JByteArray<'a>, AppErr> {
        let array = env.new_byte_array(self.buf.len() as i32)?;
        let buf: &[u8] = &self.buf;
        env.set_byte_array_region(&array, 0, unsafe {
            transmute(buf)
        })?;
        Ok(array)
    }

    pub fn new_jbyte_array<'a>(&self, env: &'a mut JNIEnv) -> JByteArray<'a> {
        let array = env.new_byte_array(self.buf.len() as i32).unwrap();
        let buf: &[u8] = &self.buf;
        let buf2: &[i8] = unsafe {
            transmute(buf)
        };
        env.set_byte_array_region(&array, 0, buf2).unwrap();
        array
    }

}
